/**
 * 跨操作系统统一接口: FatFS 文件系统适配
 */

#include <stdio.h>
#include "unios.h"
#include "utils.h"
#include "ff.h"

#include "log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "unios"
#include "log4app.h"

// ======================================================================================
// 关于文件
// ======================================================================================

unios_file_t unios_file_null() {
    return 0;
}

bool unios_file_is_valid(unios_file_t uf) {
    return uf != 0;
}

int64_t unios_file_size(const char *path) {
    FILINFO fst;
    if(f_stat(path, &fst) != FR_OK) { return -1; }
    return fst.fsize;
}

std_err_t unios_file_rename(const char *from, const char *to) {
    if(f_rename(from, to) != FR_OK) { return STD_ERR_FAIL; }
    return STD_ERR_OK;
}

std_err_t unios_file_delete(const char *path) {
    if(f_unlink(path) != FR_OK) { return STD_ERR_FAIL; }
    return STD_ERR_OK;
}

unios_file_t unios_file_from_stdio_stream(FILE *fp) {
    return unios_file_null();
}

std_err_t unios_file_open(const char *path, unios_file_oflag_t oflag, unios_file_t *uf) {
    if(!path || !uf) return STD_ERR_INVALID_ARG;
    *uf = unios_file_null();

    FIL *fp = (FIL*)unios_malloc0(sizeof(FIL));
    if(!fp) return STD_ERR_NO_MEM;
    
    FRESULT fres;
    if(oflag == UNIOS_FILE_OFLAG_READ) {
    	fres = f_open(fp, path, FA_READ);
    } else if(oflag == UNIOS_FILE_OFLAG_APPEND) {
        fres = f_open(fp, path, FA_OPEN_APPEND);
    } else if(oflag == UNIOS_FILE_OFLAG_WRITE_TRUNCATE) {
    	fres = f_open(fp, path, FA_WRITE | FA_CREATE_ALWAYS);
    } else if(oflag == UNIOS_FILE_OFLAG_READ_WRITE) {
    	fres = f_open(fp, path, FA_READ | FA_WRITE | FA_CREATE_ALWAYS);
    } else if(oflag == UNIOS_FILE_OFLAG_DEVICE_IO) {
    	return STD_ERR_NOT_SUPPORTED;
    } else {
    	return STD_ERR_INVALID_ARG;
    }

    if(fres != FR_OK) return STD_ERR_FAIL;
    *uf = (unios_file_t)fp;
    return STD_ERR_OK;
}

void unios_file_destroy(unios_file_t *uf) {
    if(!uf || !unios_file_is_valid(*uf)) return;
    f_close((FIL*)(*uf));
    unios_free((void*)(*uf));
    *uf = unios_file_null();
}

int64_t unios_file_datasize(unios_file_t uf) {
    if(!unios_file_is_valid(uf)) return -1;
    return f_size((FIL*)uf);
}

std_err_t unios_file_sync(unios_file_t uf) {
    if(!unios_file_is_valid(uf)) return STD_ERR_INVALID_ARG;
    return (f_sync((FIL*)uf) == FR_OK) ? STD_ERR_OK : STD_ERR_FAIL;
}

std_err_t unios_file_read(unios_file_t uf, void *buf, size_t nbyte, int64_t offset, size_t *rbyte) {
    if(!unios_file_is_valid(uf) || !buf || !nbyte) return STD_ERR_INVALID_ARG;
    if(rbyte) *rbyte = 0;
    UINT br = 0;
    
    // 设置文件当前偏移
    if(offset >= 0 && f_lseek((FIL*)uf, offset) != FR_OK) return STD_ERR_FAIL;
    // 读取文件
    if(f_read((FIL*)uf, buf, nbyte, &br) != FR_OK) return STD_ERR_FAIL;

    if(rbyte) *rbyte = br;
    return STD_ERR_OK;
}

std_err_t unios_file_write(unios_file_t uf, const void *buf, size_t nbyte, int64_t offset, size_t *wbyte) {
    if(!unios_file_is_valid(uf) || !buf || !nbyte) return STD_ERR_INVALID_ARG;
    if(wbyte) *wbyte = 0;
    UINT bw = 0;
    
    // 设置文件当前偏移
    if(offset >= 0 && f_lseek((FIL*)uf, offset) != FR_OK) return STD_ERR_FAIL;
    // 写入文件
    if(f_write((FIL*)uf, buf, nbyte, &bw) != FR_OK) return STD_ERR_FAIL;

    if(wbyte) *wbyte = bw;
    return STD_ERR_OK;
}
